﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;
using System.Drawing;

namespace DailyPhoto.Faces
{
    public class FaceDetector
    {
        private const double Scale = 1;
        private const double ScaleFactorFirst = 1.05;
        private const double ScaleFactorSecond = 1.01;
        private const int MinNeighborsFirst = 50;
        private const int MinNeighborsSecond = 3;

        public CvHaarClassifierCascade HaarCascade { get; protected set; }

        public FaceDetector(string haarCascadeFileName)
        {
            string path = Environment.GetEnvironmentVariable("PATH");
            string dllPath = "C:\\OpenCV\\;";
            if (!path.Contains(dllPath))
            {
                path = dllPath + path;
                Environment.SetEnvironmentVariable("PATH", path);
            }

            this.HaarCascade = CvHaarClassifierCascade.FromFile(haarCascadeFileName);
        }

        public List<Face> FindFaces(Bitmap image)
        {
            List<Face> results = new List<Face>();

            using (IplImage img = IplImage.FromBitmap(image))
            {
                using (IplImage smallImg = new IplImage(new CvSize(Cv.Round(img.Width / Scale), Cv.Round(img.Height / Scale)), BitDepth.U8, 1))
                {
                   using (IplImage gray = new IplImage(img.Size, BitDepth.U8, 1))
                    {
                        Cv.CvtColor(img, gray, ColorConversion.BgrToGray);
                        Cv.Resize(gray, smallImg, Interpolation.Linear);
                        Cv.EqualizeHist(smallImg, smallImg);
                    }
               
                    using (CvMemStorage storage = new CvMemStorage())
                    {
                        storage.Clear();

                        CvSeq<CvAvgComp> faces = Cv.HaarDetectObjects(smallImg, this.HaarCascade, storage, 1.2, 2, 0, new CvSize(20, 20));

                        for (int i = 0; i < faces.Total; i++)
                        {
                            Face face = new Face(ToRectangle(faces[i].Value.Rect));
                            results.Add(face);

                        }
                    }
                    return results;

                    /*
                    if (results.Count == 0)
                    {
                        using (CvMemStorage storage = new CvMemStorage())
                        {
                            storage.Clear();

                            CvSeq<CvAvgComp> faces = Cv.HaarDetectObjects(smallImg, this.HaarCascade, storage, ScaleFactorSecond, MinNeighborsSecond, HaarDetectionType.Zero, new CvSize(10, 10));

                            for (int i = 0; i < faces.Total; i++)
                            {
                                Face face = new Face(ToRectangle(faces[i].Value.Rect));
                                results.Add(face);
                            }
                        }
                    }*/
                }
            }

            
            /*
            List<Face> filteredResults = new List<Face>();

            while (results.Count > 0)
            {
                Face face = results[0];
                results.Remove(face);
                foreach (Face otherFace in results.FindAll(f => f.Bounds.IntersectsWith(face.Bounds)))
                {
                    int left = Math.Min(face.Bounds.Left, otherFace.Bounds.Left);
                    int top = Math.Min(face.Bounds.Top, otherFace.Bounds.Top);
                    int right = Math.Max(face.Bounds.Right, otherFace.Bounds.Right);
                    int bottom = Math.Max(face.Bounds.Bottom, otherFace.Bounds.Bottom);
                    face.Bounds = new Rectangle(new Point(left, top), new Size(right - left, bottom - top));
                    results.Remove(otherFace);
                }
                filteredResults.Add(face);
            }

            return filteredResults;*/

        }

        public static Rectangle ToRectangle(CvRect r)
        {
            return new Rectangle(r.X, r.Y, r.Width, r.Height);
        }

    }
}
